﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Input;

namespace CitiSurface
{
    /// <summary>
    /// Interaction logic for ShellViewWindow.xaml
    /// </summary>
    public partial class ShellView : SurfaceWindow
    {
        /// <summary>
        /// Default constructor.
        /// </summary>
        public ShellView()
        {
            InitializeComponent();

            // Add handlers for window availability events
            AddWindowAvailabilityHandlers();
        }

        /// <summary>
        /// Occurs when the window is about to close. 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            // Remove handlers for window availability events
            RemoveWindowAvailabilityHandlers();
        }

        /// <summary>
        /// Adds handlers for window availability events.
        /// </summary>
        private void AddWindowAvailabilityHandlers()
        {
            // Subscribe to surface window availability events
            ApplicationServices.WindowInteractive += OnWindowInteractive;
            ApplicationServices.WindowNoninteractive += OnWindowNoninteractive;
            ApplicationServices.WindowUnavailable += OnWindowUnavailable;
        }

        /// <summary>
        /// Removes handlers for window availability events.
        /// </summary>
        private void RemoveWindowAvailabilityHandlers()
        {
            // Unsubscribe from surface window availability events
            ApplicationServices.WindowInteractive -= OnWindowInteractive;
            ApplicationServices.WindowNoninteractive -= OnWindowNoninteractive;
            ApplicationServices.WindowUnavailable -= OnWindowUnavailable;
        }

        /// <summary>
        /// This is called when the user can interact with the application's window.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWindowInteractive(object sender, EventArgs e)
        {
            //TODO: enable audio, animations here
        }

        /// <summary>
        /// This is called when the user can see but not interact with the application's window.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWindowNoninteractive(object sender, EventArgs e)
        {
            //TODO: Disable audio here if it is enabled

            //TODO: optionally enable animations here
        }

        /// <summary>
        /// This is called when the application's window is not visible or interactive.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnWindowUnavailable(object sender, EventArgs e)
        {
            //TODO: disable audio, animations here
        }

        private DispatcherTimer _timer;
        private int _tickerCount;

        private void SurfaceWindowLoaded(object sender, RoutedEventArgs e)
        {
            //Initialize VisualizationDefinition
            var viewModel = DataContext as ShellViewModel;
            if (viewModel != null)
            {
                //Initialize TagVisualizationDefinition so the tag can be recognize
                //Need to be trigger every time Interviewee has been loaded
                foreach (var interviewee in viewModel.Interviewees)
                {
                    var tag = new TagVisualizationDefinition
                    {
                        Value = interviewee.TagValue,
                        Source = new Uri("IdTagVisualization.xaml", UriKind.Relative),
                        MaxCount = 1,
                        LostTagTimeout = 2000.0,//2 seconds
                        OrientationOffsetFromTag = 0,
                        PhysicalCenterOffsetFromTag = new Vector(-0.5F, -1.0),
                        TagRemovedBehavior = TagRemovedBehavior.Fade,
                        UsesTagOrientation = true
                    };

                    MainTagVisualier.Definitions.Add(tag);
                }

                //Set CurrShowing Item in the TileGrid
                var docItems = viewModel.DocItems;
                if (docItems != null && docItems.Any())
                {
                    viewModel.CurrShowingDocItem = docItems[0];
                }
                var albumItems = viewModel.AlbumItems;
                if (albumItems != null && albumItems.Any())
                {
                    viewModel.CurrShowingImageItem = albumItems[0];
                }
                var videoItems = viewModel.VideoItems;
                if (videoItems != null && videoItems.Any())
                {
                    viewModel.CurrShowingVideoItem = videoItems[0];
                }

                //Start Ticker for auto switch the currShowingItem
                _tickerCount = 0;
                _timer = new DispatcherTimer();
                _timer.Interval = TimeSpan.FromSeconds(10);
                _timer.Tick += TimerTick;
                _timer.Start();
            }

            // TODO register interviewee collection changed event here
        }

        //Timer Ticked, switch the currentShowingItem
        void TimerTick(object sender, EventArgs e)
        {
            var viewModel = DataContext as ShellViewModel;
            if (viewModel != null)
            {
                switch (_tickerCount % 3)
                {
                    case 0:
                        {
                            var albumItems = viewModel.AlbumItems;
                            if (albumItems != null && albumItems.Any())
                            {
                                if (viewModel.CurrShowingImageItem == null)
                                    viewModel.CurrShowingImageItem = albumItems[0];
                                else
                                {
                                    var currIdx = albumItems.IndexOf(viewModel.CurrShowingImageItem);
                                    currIdx = (currIdx + 1) % albumItems.Count;
                                    viewModel.CurrShowingImageItem = albumItems[currIdx];
                                }

                                var fadein = (Storyboard)FindResource("fadein");
                                fadein.Begin(PicBarImage);
                            }
                        }
                        break;
                    case 1:
                        {
                            var videoItems = viewModel.VideoItems;
                            if (videoItems != null && videoItems.Any())
                            {
                                if (viewModel.CurrShowingVideoItem == null)
                                    viewModel.CurrShowingVideoItem = videoItems[0];
                                else
                                {
                                    var currIdx = videoItems.IndexOf(viewModel.CurrShowingVideoItem);
                                    currIdx = (currIdx + 1) % videoItems.Count;
                                    viewModel.CurrShowingVideoItem = videoItems[currIdx];
                                }

                                var fadein = (Storyboard)FindResource("fadein");
                                fadein.Begin(VideoBarImage);
                            }
                        }
                        break;
                    case 2:
                        {
                            var docItems = viewModel.DocItems;
                            if (docItems != null && docItems.Any())
                            {
                                if (viewModel.CurrShowingDocItem == null)
                                    viewModel.CurrShowingDocItem = docItems[0];
                                else
                                {
                                    var currIdx = docItems.IndexOf(viewModel.CurrShowingDocItem);
                                    currIdx = (currIdx + 1) % docItems.Count;
                                    viewModel.CurrShowingDocItem = docItems[currIdx];
                                }

                                var fadein = (Storyboard)FindResource("fadein");
                                fadein.Begin(ArticleBarImage);
                            }
                        }
                        break;
                }
                _tickerCount++;
            }
        }

        /// <summary>
        /// This is called when the visualization object been added into MainTagVisualizer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainTagVisualierVisualizationAdded(object sender, TagVisualizerEventArgs e)
        {
            //get the tagValue
            var tagValue = e.TagVisualization.VisualizedTag.Value;

            //Using database part
            //where amazing happens -lei
            //check sqlserver
            //Interviewee newInterviewee = null;
            //if (CitiSurface.Model.DataUtilities.existSqlServerService())
            //{
                //read from DB
                //string strCon = @"server=(local);database=CitiSurfaceData;uid=admin;pwd=sqladmin;";
                //SqlConnection myCon = new SqlConnection(strCon);

                //try
                //{
                   //myCon.Open();

                    //newInterviewee = CitiSurface.Model.DataUtilities.getInterviewee(tagValue, myCon);
                //}
                //catch (Exception ex)
                //{
                    //Console.WriteLine("{0}", ex.ToString());
                //}
                //finally
                //{
                    //myCon.Close();
                //}
            //}
            //else
            //{
                //Console.WriteLine("No Database Service!");
            //}
            //end of where amazing happens -lei

            Interviewee interviewee = null;
            var shellViewModel = DataContext as ShellViewModel;
            //retrieve corresponding interviewee object and set to DataContext
            if (shellViewModel != null)
                interviewee = shellViewModel.GetInterviewee((int) tagValue);
            
            //Possible change: instead of setting Interviewee object to datacontext, set a viewmodel class
            e.TagVisualization.DataContext = new IdTagViewModel
                                                 {
                                                     Interviewee = interviewee
                                                 };
        }

        private void ScatterLayerDrop(object sender, SurfaceDragDropEventArgs e)
        {
            var targetScatterView = sender as ScatterView;
            var viewModel = DataContext as ShellViewModel;
            //Check if it is in the scatterviewitems already
            if(viewModel != null && e.Cursor.Data != null && e.Cursor.Data is IMainBarItem)
            {
                var cloneObj = ((IMainBarItem) e.Cursor.Data).Clone();
                viewModel.ScatterViewItems.Add((IMainBarItem) cloneObj);

                var svi = targetScatterView.ItemContainerGenerator.ContainerFromItem(cloneObj) as ScatterViewItem;
                svi.Visibility = System.Windows.Visibility.Visible;
                svi.Center = e.Cursor.GetPosition(targetScatterView);
                svi.Orientation = e.Cursor.GetOrientation(targetScatterView);
                svi.Loaded += (s, re) => checkAndMoveOverlapedScatterViewItem(svi, ControlPanel);
            }

            e.Handled = true;
        }

        //Deprecated
        private void ScatterLayerPreviewTouchDown(object sender, TouchEventArgs e)
        {
            var findSource = e.OriginalSource as FrameworkElement;
            ScatterViewItem draggedElement = null;

            // Find the ScatterViewitem object that is being touched.
            while (draggedElement == null && findSource != null)
            {
                if ((draggedElement = findSource as ScatterViewItem) == null)
                {
                    findSource = VisualTreeHelper.GetParent(findSource) as FrameworkElement;
                }
            }

            if (draggedElement == null)
            {
                return;
            }

            var data = draggedElement.Content as IMainBarItem;

            // Create the cursor visual
            var cursorVisual = new ContentControl()
            {
                Content = draggedElement.DataContext,
                Style = FindResource("CursorStyle") as Style
            };

            // Create a list of input devices. Add the touches that
            // are currently captured within the dragged element and
            // the current touch (if it isn't already in the list).
            var devices = new List<InputDevice>();
            devices.Add(e.TouchDevice);
            foreach (TouchDevice touch in draggedElement.TouchesCapturedWithin)
            {
                if (touch != e.TouchDevice)
                {
                    devices.Add(touch);
                }
            }

            // Get the drag source object
            var dragSource = ItemsControl.ItemsControlFromItemContainer(draggedElement);

            SurfaceDragDrop.BeginDragDrop(
                    dragSource,                     // The ScatterView object that the cursor is dragged out from.
                          draggedElement,                 // The ScatterViewItem object that is dragged from the drag source.
                          cursorVisual,                   // The visual element of the cursor.
                          draggedElement.DataContext,     // The data attached with the cursor.
                          devices,                        // The input devices that start dragging the cursor.
                          DragDropEffects.Move);          // The allowed drag-and-drop effects of this operation.

            // Prevents the default touch behavior from happening
            // and disrupting our code.
            e.Handled = true;

            // Hide the ScatterViewItem for now. We will remove it
            // if the DragDrop is successful.
            draggedElement.Visibility = Visibility.Hidden;
        }

        //Deprecated
        private void ScatterLayerDragCanceled(object sender, SurfaceDragDropEventArgs e)
        {
            var data = e.Cursor.Data as IMainBarItem;
            var svi =
                ((ScatterView) sender).ItemContainerGenerator.ContainerFromItem(data) as ScatterViewItem;
            if( svi != null)
                svi.Visibility = Visibility.Visible;
        }

        //Deprecated
        private void ScatterLayerDragCompleted(object sender, SurfaceDragCompletedEventArgs e)
        {
            if (e.Cursor.CurrentTarget != sender && e.Cursor.Effects == DragDropEffects.Move)
            {
                var viewModel = DataContext as ShellViewModel;
                if (viewModel != null) viewModel.ScatterViewItems.Remove(e.Cursor.Data as IMainBarItem);
                e.Handled = true;
            }
        }

        //Close the ScatterViewItem, Triggered when the cancelButton of the control has been clicked
        //Linked in the Xaml
        private void ScatterViewItemClose(object sender, RoutedEventArgs e)
        {
            var item = ((IMainBarItem) ((UserControl)e.Source).DataContext);
            var viewModel = DataContext as ShellViewModel;
            viewModel.ScatterViewItems.Remove(item);
        }

        //Slide Back the TileGrid
        private void ControlBackButtonClick(object sender, RoutedEventArgs e)
        {
            var backStoryboard = (Storyboard)FindResource("TileSlidetIn");
            backStoryboard.Begin();
            ControlContainer.Visibility = Visibility.Collapsed;
        }

        //Picture Tile has been touched
        private void PicBarViewTouchUp(object sender, TouchEventArgs e)
        {
            foreach (UIElement elm in ControlContainer.Children)
            {
                elm.Visibility = Visibility.Collapsed;
            }
            ImageBar.Visibility = Visibility.Visible;
            ControlContainer.Visibility = Visibility.Visible;
        }

        //Article/Document Tile has been touched
        private void ArticleBarViewTouchUp(object sender, TouchEventArgs e)
        {
            foreach (UIElement elm in ControlContainer.Children)
            {
                elm.Visibility = Visibility.Collapsed;
            }
            DocBar.Visibility = Visibility.Visible;
            ControlContainer.Visibility = Visibility.Visible;
        }

        //Video Tile has been touched
        private void VideoBarViewTouchUp(object sender, TouchEventArgs e)
        {
            foreach(UIElement elm in ControlContainer.Children)
            {
                elm.Visibility = Visibility.Collapsed;
            }
            VideoBar.Visibility = Visibility.Visible;
            ControlContainer.Visibility = Visibility.Visible;
        }

        //Get the bounds of element"of" inside element"from"
        private Rect GetBounds(FrameworkElement of, FrameworkElement from)
        {
            var transform = of.TransformToVisual(from);
            return transform.TransformBounds(new Rect(0, 0, of.ActualWidth, of.ActualHeight));
        }

        //Check if the scatterViewItem are overlaped with the ControlGrid
        private void checkAndMoveOverlapedScatterViewItem(ScatterViewItem svi, FrameworkElement fixTarget)
        {
            var senderBound = GetBounds(svi, MainContainer);
            var targetBound = GetBounds(fixTarget, MainContainer);

            var intersect = senderBound.IntersectsWith(targetBound);
            //check intersection
            if (!intersect) return;

            var senderBoundLoc = svi.ActualCenter;
            var tileBoundLoc = new Point(targetBound.Location.X+targetBound.Width/2, targetBound.Location.Y+targetBound.Height/2);

            //decide which Side should the elemnt move out the control grid
            double movingX = 0, movingY = 0;

            if(senderBound.Left >= targetBound.Left && senderBound.Right <= targetBound.Right)
            {
                //only Y move
                movingY = (senderBoundLoc.Y > tileBoundLoc.Y)
                              ? targetBound.Bottom - senderBound.Top
                              : targetBound.Top - senderBound.Bottom;
            }else
            {
                //x move
                movingX = (senderBoundLoc.X > tileBoundLoc.X)
                              ? targetBound.Right - senderBound.Left
                              : targetBound.Left - senderBound.Right;

            }

            //create animation
            var animate = new PointAnimation();
            var endPoint = new Point(svi.ActualCenter.X+movingX, svi.ActualCenter.Y+movingY);
            animate.Duration = new Duration(TimeSpan.FromSeconds(0.5));
            animate.From = svi.ActualCenter;
            animate.To = endPoint;
            animate.FillBehavior = FillBehavior.Stop;
            svi.BeginAnimation(ScatterViewItem.CenterProperty, animate);
            svi.Center = endPoint;
        }

        //Trigger the check event when the scatterViewItem manipulation completed
        private void ScatterViewItemContainerManipulationCompleted(object sender, ContainerManipulationCompletedEventArgs e)
        {
            var scatterItem = sender as ScatterViewItem;
            checkAndMoveOverlapedScatterViewItem(scatterItem, ControlPanel);
        }

        //During ControlPanel's Rotation
        private void ControlPanelManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {

            ControlPanelRotate.Angle += e.DeltaManipulation.Rotation;
        }

        //Rotation Starting
        private void ControlPanelManipulationStarting(object sender, ManipulationStartingEventArgs e)
        {

            UIElement container = this as UIElement;

            e.Handled = true;
            e.Mode = ManipulationModes.Rotate;
            e.ManipulationContainer = container;
            e.IsSingleTouchEnabled = true;

            Point localCenter = new Point(ControlPanel.ActualWidth / 2, ControlPanel.ActualHeight / 2);
            Point LocalCenterInContainer = ControlPanel.TranslatePoint(localCenter, container);
            e.Pivot = new ManipulationPivot(LocalCenterInContainer, Math.Max(ControlPanel.ActualWidth / 2, ControlPanel.ActualHeight / 2));
        }

        //After Rotation ended, Inertia starting
        private void ControlPanelManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
        {
            e.RotationBehavior = new InertiaRotationBehavior
            {
                InitialVelocity = e.InitialVelocities.AngularVelocity,
                DesiredDeceleration = 0.001
            };
        }

        //When the ControlGrid has been clicked (On the current element of the cover flow)
        private void ControlPanelClickElement(object sender, RoutedEventArgs e)
        {
            var targetBarControl = sender as IBarControl;
            var viewModel = DataContext as ShellViewModel;
            //Check if it is in the scatterviewitems already
            if (viewModel != null && targetBarControl != null && targetBarControl.SelectedItem is IMainBarItem)
            {
                //clone the current selected object
                var cloneObj = ((IMainBarItem)targetBarControl.SelectedItem).Clone();
                viewModel.ScatterViewItems.Add((IMainBarItem)cloneObj);

                var svi = TopScatterLayer.ItemContainerGenerator.ContainerFromItem(cloneObj) as ScatterViewItem;
                svi.Visibility = System.Windows.Visibility.Visible;

                //Automatically push it out the control grid
                var targetUserControl = sender as UserControl;
                if (targetUserControl != null)
                {
                    var localCenter = new Point(targetUserControl.ActualWidth/2, targetUserControl.ActualHeight/2 + 10);

                    svi.Center = targetUserControl.TranslatePoint(localCenter, TopScatterLayer);
                    svi.Orientation = 1;
                    svi.Loaded += (s, re) => checkAndMoveOverlapedScatterViewItem(svi, ControlPanel);
                }
            }

            e.Handled = true;
        }
    }
}
